#pragma once
#include "Runtime/CGI/CGIPrefix.h"
#include "VulkanQueue.h"

namespace Alice
{
    class FStagingManager;
}

namespace Alice{
    class CGIVulkanCommandListContextImmediate;
    class FenceManager;
    class FMemoryManager;
    class FDeviceMemoryManager;
    class VulkanRenderPassManager;
    struct FPixelFormatInfo;
    enum class EPixelFormatCapabilities : uint32;
    struct FOptionalVulkanDeviceExtensions
	{
		union
		{
			struct
			{
				// Optional Extensions
				uint64 HasEXTValidationCache : 1;
				uint64 HasMemoryPriority : 1;
				uint64 HasMemoryBudget : 1;
				uint64 HasEXTASTCDecodeMode : 1;
				uint64 HasEXTFragmentDensityMap : 1;
				uint64 HasEXTFragmentDensityMap2 : 1;
				uint64 HasKHRFragmentShadingRate : 1;
				uint64 HasKHRFragmentShaderBarycentric : 1;
				uint64 HasEXTFullscreenExclusive : 1;
				uint64 HasImageAtomicInt64 : 1;
				uint64 HasAccelerationStructure : 1;
				uint64 HasRayTracingPipeline : 1;
				uint64 HasRayQuery : 1;
				uint64 HasDeferredHostOperations : 1;
				uint64 HasEXTCalibratedTimestamps : 1;
				uint64 HasEXTDescriptorBuffer : 1;
				uint64 HasEXTDeviceFault : 1;
				uint64 HasEXTLoadStoreOpNone : 1;
				uint64 HasEXTMeshShader : 1;

				// Vendor specific
				uint64 HasAMDBufferMarker : 1;
				uint64 HasNVDiagnosticCheckpoints : 1;
				uint64 HasNVDeviceDiagnosticConfig : 1;
				uint64 HasANDROIDExternalMemoryHardwareBuffer : 1;

				// Promoted to 1.1
				uint64 HasKHRMultiview : 1;
				uint64 HasKHR16bitStorage : 1;
				uint64 HasKHRSamplerYcbcrConversion : 1;

				// Promoted to 1.2
				uint64 HasKHRRenderPass2 : 1;
				uint64 HasKHRImageFormatList : 1;
				uint64 HasKHRShaderAtomicInt64 : 1;
				uint64 HasEXTScalarBlockLayout : 1;
				uint64 HasBufferDeviceAddress : 1;
				uint64 HasSPIRV_14 : 1;
				uint64 HasShaderFloatControls : 1;
				uint64 HasKHRShaderFloat16 : 1;
				uint64 HasEXTDescriptorIndexing : 1;
				uint64 HasEXTShaderViewportIndexLayer : 1;
				uint64 HasSeparateDepthStencilLayouts : 1;
				uint64 HasEXTHostQueryReset : 1;
				uint64 HasQcomRenderPassShaderResolve : 1;
				uint64 HasKHRDepthStencilResolve : 1;

				// Promoted to 1.3
				uint64 HasEXTTextureCompressionASTCHDR : 1;
				uint64 HasKHRMaintenance4 : 1;
				uint64 HasKHRSynchronization2 : 1;
				uint64 HasEXTSubgroupSizeControl : 1;
				uint64 HasEXTPipelineCreationCacheControl : 1;
			};
			uint64 Packed;
		};

		FOptionalVulkanDeviceExtensions()
		{
			static_assert(sizeof(Packed) == sizeof(FOptionalVulkanDeviceExtensions), "More bits needed for Packed!");
			Packed = 0;
		}

		inline bool HasGPUCrashDumpExtensions() const
		{
			return HasAMDBufferMarker || HasNVDiagnosticCheckpoints;
		}

		inline bool HasRaytracingExtensions() const
		{
			return 
				HasAccelerationStructure && 
				(HasRayTracingPipeline || HasRayQuery) &&
				HasEXTDescriptorIndexing &&
				HasBufferDeviceAddress && 
				HasDeferredHostOperations && 
				HasSPIRV_14 && 
				HasShaderFloatControls;
		}
	};
    // All the features and properties we need to keep around from extension initialization
    struct FOptionalVulkanDeviceExtensionProperties
    {
        FOptionalVulkanDeviceExtensionProperties()
        {
            memset(this,0,sizeof(FOptionalVulkanDeviceExtensionProperties));
        }

        VkPhysicalDeviceDriverPropertiesKHR PhysicalDeviceDriverProperties;

        VkPhysicalDeviceDescriptorBufferPropertiesEXT DescriptorBufferProps;
        VkPhysicalDeviceSubgroupSizeControlPropertiesEXT SubgroupSizeControlProperties;

        VkPhysicalDeviceAccelerationStructurePropertiesKHR AccelerationStructureProps;
        VkPhysicalDeviceRayTracingPipelinePropertiesKHR RayTracingPipelineProps;

        VkPhysicalDeviceFragmentShadingRateFeaturesKHR FragmentShadingRateFeatures;
        VkPhysicalDeviceFragmentDensityMapFeaturesEXT FragmentDensityMapFeatures;
        VkPhysicalDeviceFragmentDensityMap2FeaturesEXT FragmentDensityMap2Features;

        VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR FragmentShaderBarycentricProps;
        VkPhysicalDeviceComputeShaderDerivativesFeaturesNV ComputeShaderDerivativesFeatures;

        VkPhysicalDeviceMeshShaderPropertiesEXT MeshShaderProperties;
    };
    class VulkanPhysicalDeviceFeatures
    {
    public:
        void Query(VkPhysicalDevice PhysicalDevice, uint32 APIVersion);
        VkPhysicalDeviceFeatures	     Core_1_0;
        VkPhysicalDeviceVulkan11Features Core_1_1;
        VkPhysicalDeviceVulkan12Features Core_1_2;
        VkPhysicalDeviceVulkan13Features Core_1_3;
    };
    class VulkanDevice{
    public:
        VkDevice mDevice;
        VkPhysicalDevice mVkPhysicalDevice;
        uint32 mAPIVersion;
        VkPhysicalDeviceProperties mVkPhysicalDeviceProperties;
        VkPhysicalDeviceIDPropertiesKHR mVkPhysicalDeviceIDProperties;
        VkPhysicalDeviceSubgroupProperties mVkPhysicalDeviceSubgroupProperties;
        std::vector<VkQueueFamilyProperties> mVkQueueFamilyProperties;
        VulkanPhysicalDeviceFeatures mVulkanPhysicalDeviceFeatures;

        VulkanQueue * mGraphicsQueue;
        VulkanQueue * mComputeQueue;
        VulkanQueue * mTransferQueue;
        VulkanQueue * mPresentQueue;
        
        CGIVulkanCommandListContextImmediate* mImmediateContext;
        VkFormatProperties mFormatProperties[VK_FORMAT_RANGE_SIZE];
        VkComponentMapping PixelFormatComponentMapping[EPixelFormat::PF_NUM];
        mutable std::unordered_map<VkFormat, VkFormatProperties> mExtensionFormatProperties;
        FenceManager*mFenceManager;
        FMemoryManager*mMemoryManager;
        FDeviceMemoryManager*mDeviceMemoryManager;
        VulkanRenderPassManager* mRenderPassManager;
        FStagingManager* mStagingManager;
    	FOptionalVulkanDeviceExtensions	OptionalDeviceExtensions;
    	FOptionalVulkanDeviceExtensionProperties OptionalDeviceExtensionProperties;
    	class FVulkanPipelineStateCacheManager* mPipelineStateCacheManager;
        VulkanDevice(VkPhysicalDevice inGPU,uint32 inAPIVersion);
        void InitGPU();
        void InitDevice();
        void SetupPresentQueue(VkSurfaceKHR inSurface);
        VulkanQueue * GetPresentQueue()const
        {
            return mPresentQueue;
        }
        inline VkPhysicalDevice GetPhysicalHandle() const
        {
            return mVkPhysicalDevice;
        }
        inline VkDevice GetDevice() const
        {
            return mDevice;
        }
        CGIVulkanCommandListContextImmediate& GetImmediateContext()
        {
            return *mImmediateContext;
        }
        inline FenceManager& GetFenceManager()
        {
            return *mFenceManager;
        }
        inline FDeviceMemoryManager& GetDeviceMemoryManager()
        {
            return *mDeviceMemoryManager;
        }
        inline FMemoryManager& GetMemoryManager()
        {
            return *mMemoryManager;
        }
    	inline FStagingManager& GetStagingManager()
        {
        	return *mStagingManager;
        }
        inline const FOptionalVulkanDeviceExtensions& GetOptionalExtensions() const
        {
            return OptionalDeviceExtensions;
        }
        inline const VkPhysicalDeviceLimits& GetLimits() const
        {
            return mVkPhysicalDeviceProperties.limits;
        }
        void SetupFormats();
        void MapBufferFormatSupport(FPixelFormatInfo& PixelFormatInfo, EPixelFormat UEFormat, VkFormat VulkanFormat);
        const VkFormatProperties& GetFormatProperties(VkFormat InFormat) const;
        void MapImageFormatSupport(FPixelFormatInfo& PixelFormatInfo, const std::vector<VkFormat>& PrioritizedFormats, EPixelFormatCapabilities RequiredCapabilities);
        void MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, EPixelFormatCapabilities RequiredCapabilities, int32 BlockBytes);
        void MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping);
        void MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, int32 BlockBytes);
        void MapFormatSupport(EPixelFormat UEFormat, std::initializer_list<VkFormat> PrioritizedFormats, const VkComponentMapping& ComponentMapping, EPixelFormatCapabilities RequiredCapabilities);
    };
}
